/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include "cmsis_os2.h"
#include "aquaponics.h"
#include "iot_errno.h"
#include "iot_gpio.h"
#include "iot_gpio_ex.h"
#include "iot_i2c.h"
#include "iot_i2c_ex.h"
#include "iot_adc.h"
#include "iot_demo_def.h"
#include "hi_time.h"

#define WIFI_IOT_I2C_IDX_1 1
#define WIFI_IOT_I2C_IDX_0 0
#define IOT_ADC_CHANNEL_6 6
#define IOT_ADC_CHANNEL_2 2
#define IOT_IO_NAME_GPIO_12 12
#define IOT_IO_NAME_GPIO_0 0
#define IOT_IO_NAME_GPIO_1 1

#define SHT30_Addr 0x44
#define BH1750_ADDR 0x23

IotGpioValue val = {0};

/***************************************************************
 * 函数名称: Init_BH1750
 * 说    明: 写命令初始化BH1750
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
static int InitBH1750(void)
{
    int ret;
    uint8_t send_data[1] = {0x01};
    ret = IoTI2cWrite(WIFI_IOT_I2C_IDX_1, (BH1750_ADDR << 1) | 0x00, send_data, 1);
    if (ret != 0)
    {
        printf("===== Error: I2C write ret = 0x%x! =====\r\n", ret);
        return -1;
    }
    return 0;
}

/***************************************************************
 * 函数名称: Start_BH1750
 * 说    明: 启动BH1750
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
static int StartBH1750(void)
{
    int ret;
    uint8_t send_data[1] = {0x10};
    ret = IoTI2cWrite(WIFI_IOT_I2C_IDX_1, (BH1750_ADDR << 1) | 0x00, send_data, 1);
    if (ret != 0)
    {
        printf("===== Error: I2C write ret = 0x%x! =====\r\n", ret);
        return -1;
    }
    return 0;
}

/***************************************************************
 * 函数名称: SHT30_reset
 * 说    明: SHT30复位
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
void SHT30_reset(void)
{
    uint8_t send_data[2] = {0x30, 0xA2};
    IoTI2cWrite(WIFI_IOT_I2C_IDX_1, (SHT30_Addr << 1) | 0x00, send_data, 2);
}

/***************************************************************
 * 函数名称: Init_SHT30
 * 说    明: 初始化SHT30，设置测量周期
 * 参    数: 无
 * 返 回 值: 无
 ***************************************************************/
int Init_SHT30(void)
{
    uint8_t send_data[2] = {0x22, 0x36};
    return IoTI2cWrite(WIFI_IOT_I2C_IDX_1, (SHT30_Addr << 1) | 0x00, send_data, 2);
}

/***************************************************************
* 函数名称: SHT3x_CheckCrc
* 说    明: 检查数据正确性
* 参    数: data：读取到的数据
                        nbrOfBytes：需要校验的数量
                        checksum：读取到的校对比验值
* 返 回 值: 校验结果，0-成功        1-失败
***************************************************************/
static uint8_t SHT3x_CheckCrc(uint8_t data[], uint8_t nbrOfBytes, uint8_t checksum)
{

    uint8_t crc = 0xFF;
    uint8_t bit = 0;
    uint8_t byteCtr;
    const int16_t POLYNOMIAL = 0x131;
    // calculates 8-Bit checksum with given polynomial
    for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
    {
        crc ^= (data[byteCtr]);
        for (bit = 8; bit > 0; --bit)
        {
            if (crc & 0x80)
                crc = (crc << 1) ^ POLYNOMIAL;
            else
                crc = (crc << 1);
        }
    }

    if (crc != checksum)
        return 1;
    else
        return 0;
}

/***************************************************************
 * 函数名称: SHT3x_CalcTemperatureC
 * 说    明: 温度计算
 * 参    数: u16sT：读取到的温度原始数据
 * 返 回 值: 计算后的温度数据
 ***************************************************************/
static float SHT3x_CalcTemperatureC(uint16_t u16sT)
{

    float temperatureC = 0;

    u16sT &= ~0x0003;

    temperatureC = (175 * (float)u16sT / 65535 - 45);

    return temperatureC;
}

/***************************************************************
 * 函数名称: SHT3x_CalcRH
 * 说    明: 湿度计算
 * 参    数: u16sRH：读取到的湿度原始数据
 * 返 回 值: 计算后的湿度数据
 ***************************************************************/
static float SHT3x_CalcRH(uint16_t u16sRH)
{

    float humidityRH = 0;

    u16sRH &= ~0x0003;

    humidityRH = (100 * (float)u16sRH / 65535);

    return humidityRH;
}

IotGpioValue GpioGetInput(int id, IotGpioValue *val)
{
    IoTGpioGetInputVal(id, val);
    return *val;
}

//复位DS18B20
void DS18B20_Rst(void)
{
    IoTGpioSetDir(IOT_IO_NAME_GPIO_12, IOT_GPIO_DIR_OUT); //设置为输出
    IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 0);          //拉低DQ
    hi_udelay(500);                                       //至少480us
    IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 1);          //拉高DQ
    hi_udelay(20);                                        // 15~60us
}

uint8_t Wait_DS18B20_ACK(void)
{
    uint8_t retry = 0;
    IoTGpioSetDir(IOT_IO_NAME_GPIO_12, IOT_GPIO_DIR_IN);
    while (GpioGetInput(IOT_IO_NAME_GPIO_12, &val) && (retry < 200))
    {
        retry++;
        hi_udelay(1);
    }
    if (retry >= 200)
    {
        return -1; //超过200us未读到低电平，无应答
    }
    else
    {
        retry = 1; // 200us内转为低电平
    }
    while ((!GpioGetInput(IOT_IO_NAME_GPIO_12, &val)) && (retry < 240))
    {
        retry++;
        hi_udelay(1);
    }
    if (retry >= 240)
    {
        return -1; //低电平超过240us，无应答
    }
    return 0; //低电平在240以内，有应答
}

//从DS18B20读取一位，返回值：1/0
uint8_t DS18B20_Read_Bit(void)
{
    uint8_t data;
    IoTGpioSetDir(IOT_IO_NAME_GPIO_12, IOT_GPIO_DIR_OUT);
    IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 0);
    hi_udelay(2);
    IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 1);
    IoTGpioSetDir(IOT_IO_NAME_GPIO_12, IOT_GPIO_DIR_IN);
    hi_udelay(12);
    if (GpioGetInput(IOT_IO_NAME_GPIO_12, &val))
        data = 1;
    else
        data = 0;
    hi_udelay(50);
    return data;
}

//从DS18B20读取一个字节，返回值：读到的数据
uint8_t DS18B20_Read_Byte(void)
{
    uint8_t i, j, data;
    data = 0;
    for (i = 0; i < 8; i++)
    {
        j = DS18B20_Read_Bit();
        data = (j << 7) | (data >> 1);
    }
    return data;
}

//写一个字节到DS18B20，dat：要写入的字节
void DS18B20_Write_Byte(uint8_t dat)
{
    uint8_t j, testb;
    IoTGpioSetDir(IOT_IO_NAME_GPIO_12, IOT_GPIO_DIR_OUT);
    for (j = 0; j < 8; j++)
    {
        testb = dat & 0x01;
        dat = dat >> 1;
        if (testb) //写1
        {
            IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 0);
            hi_udelay(2);
            IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 1);
            hi_udelay(60);
        }
        else //写0
        {
            IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 0);
            hi_udelay(60);
            IoTGpioSetOutputVal(IOT_IO_NAME_GPIO_12, 1);
            hi_udelay(2);
        }
    }
}

//开始温度转换
void DS18B20_Start(void)
{
    DS18B20_Rst();
    Wait_DS18B20_ACK();
    DS18B20_Write_Byte(0xcc); //跳过64位ROM地址
    DS18B20_Write_Byte(0x44); //启动温度转换
}

uint8_t InitDS18B20()
{
    //设置GPIO_12的复用功能为普通GPIO
    if (IoTGpioSetFunc(IOT_IO_NAME_GPIO_12, IOT_GPIO_FUNC_GPIO_12_GPIO) != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "IoTGpioSetFunc IOT_GPIO_FUNC_GPIO_11_GPIO failed! \n");
        return -1;
    }
    IoTGpioSetPull(IOT_IO_NAME_GPIO_12, IOT_GPIO_PULL_UP);//使总线空闲时处于高电平
    DS18B20_Rst();
    return Wait_DS18B20_ACK();
}

static int InitAirSensor()
{
    // GPIO_0复用为I2C1_SDA
    if (IoTGpioSetFunc(IOT_IO_NAME_GPIO_0, IOT_GPIO_FUNC_GPIO_0_I2C1_SDA) != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "IoTGpioSetFunc IOT_GPIO_FUNC_GPIO_0_I2C1_SDA failed! \n");
        return -1;
    }

    // GPIO_1复用为I2C1_SCL
    if (IoTGpioSetFunc(IOT_IO_NAME_GPIO_1, IOT_GPIO_FUNC_GPIO_1_I2C1_SCL) != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "IoTGpioSetFunc IOT_GPIO_FUNC_GPIO_1_I2C1_SCL failed! \n");
        return -1;
    }

    // baudrate: 400kbps
    if (IoTI2cInit(WIFI_IOT_I2C_IDX_1, 400000) != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "IoTI2cInit failed! \n");
        return -1;
    }

    if (Init_SHT30() != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "Init_SHT30 failed! \n");
        return -1;
    }
    if (InitBH1750() != 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "Init_BH1750 failed! \n");
        return -1;
    }
    return 0;
}

static int InitTDSSensor()
{
    return 0;
}

static int InitPHSensor()
{
    return 0;
}

int AquaponicsInit(void)
{
    if (InitAirSensor() < 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "InitAirSensor failed! \n");
        return -1;
    }
    if (InitDS18B20() < 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "InitDS18B20 failed! \n");
        return -1;
    }
    if (InitTDSSensor() < 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "InitTDSSensor failed! \n");
        return -1;
    }
    if (InitPHSensor() < 0)
    {
        RaiseLog(LOG_LEVEL_ERR, "InitPHSensor failed! \n");
        return -1;
    }
    return 0;
}

int AquaponicsGetAirStatus(int *temperature, int *humidity, int *lux)
{
    int ret = -1;
    uint8_t data[3];
    uint16_t dat, tmp;
    uint8_t SHT3X_Data_Buffer[6];

    int rets;
    float intensity;
    rets = StartBH1750();
    if (rets != 0)
    {
        printf("Start BH1750 failed!\r\n");
        return -1;
    }
    usleep(180000);
    uint8_t recv_data[2] = {0};
    rets = IoTI2cRead(WIFI_IOT_I2C_IDX_1, (BH1750_ADDR << 1) | 0x01, recv_data, 2);
    if (rets != 0)
    {
        return -1;
    }
    intensity = (float)(((recv_data[0] << 8) + recv_data[1]) / 1.2);
    *lux = (int)intensity;

    IotI2cData sht30_i2c_data = {0};
    uint8_t send_data[2] = {0xE0, 0x00};

    sht30_i2c_data.sendBuf = send_data;
    sht30_i2c_data.sendLen = 2;
    sht30_i2c_data.receiveBuf = SHT3X_Data_Buffer;
    sht30_i2c_data.receiveLen = 6;
    IoTI2cWriteread(WIFI_IOT_I2C_IDX_1, (SHT30_Addr << 1) | 0x00, &sht30_i2c_data);

    data[0] = SHT3X_Data_Buffer[0];
    data[1] = SHT3X_Data_Buffer[1];
    data[2] = SHT3X_Data_Buffer[2];

    tmp = SHT3x_CheckCrc(data, 2, data[2]);
    if (!tmp)
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        *temperature = SHT3x_CalcTemperatureC(dat);
    }

    data[0] = SHT3X_Data_Buffer[3];
    data[1] = SHT3X_Data_Buffer[4];
    data[2] = SHT3X_Data_Buffer[5];

    tmp = SHT3x_CheckCrc(data, 2, data[2]);
    if (!tmp)
    {
        dat = ((uint16_t)data[0] << 8) | data[1];
        *humidity = SHT3x_CalcRH(dat);
        ret = 0;
    }

    return ret;
}

int AquaponicsGetTDSStatus(int *TDS)
{
    int ret = -1;
    unsigned short adc;

    ret = IoTAdcRead(IOT_ADC_CHANNEL_6, &adc, IOT_ADC_EQU_MODEL_8, IOT_ADC_CUR_BAIS_DEFAULT, 0xff);
    if (ret == IOT_SUCCESS)
    {
        *TDS = (int)((float)adc * 1.8 * 4 / 4096.0 * 434.8);
        ret = 0;
    }

    return ret;
}

int AquaponicsGetPHStatus(float *PH)
{
    int ret = -1;
    unsigned short adc;

    ret = IoTAdcRead(IOT_ADC_CHANNEL_2, &adc, IOT_ADC_EQU_MODEL_8, IOT_ADC_CUR_BAIS_DEFAULT, 0xff);
    if (ret == IOT_SUCCESS)
    {
        *PH = (float)adc * 1.8 * 4 / 4096.0 * 2.8;
        ret = 0;
    }

    return ret;
}

//从DS18B20得到温度值，精度：0.1℃，返回值：温度值（-550~1250）
int DS18B20_Get_Temp(short *watertemp)
{
    int ret = -1;
    uint8_t TL, TH;
    short temp = 0;
    DS18B20_Start(); //开始转换
    DS18B20_Rst();
    Wait_DS18B20_ACK();
    DS18B20_Write_Byte(0xcc); //跳过64位ROM地址
    DS18B20_Write_Byte(0xbe); //读取暂存器数据
    TL = DS18B20_Read_Byte(); // LSB
    TH = DS18B20_Read_Byte(); // MSB
    temp = TH;                //获得高八位
    temp <<= 8;
    temp += TL; //获得低八位
    //分辨率为0.0625
    if (TH > 7) //温度为负
        temp = -((~temp + 1) * 0.625);
    else
        temp = temp * 0.625;
    *watertemp = temp;
    ret = 0;
    return ret;
}

void AquaponicsDeinit(void)
{
}
